﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Xml.Linq;
using AzukiMap.Map;
using AzukiMap.MapSource;
using AzukiMap.Request;
using AzukiMap.Tools.Logger;
using GeoAPI.Geometries;
using GisSharpBlog.NetTopologySuite.Geometries;
using AzukiMap.Renderer;

namespace AzukiMap.Layer
{
    public class WMSLayer : RasterLayer
    {
        WMSSource _wmsMapSource;
        WMSRequest _wmsRequest;
        RasterRenderer _renderer;
        private IList<IList<FrameworkElement>> _renderedImage;

        public WMSLayer()
        {
            
        }

        public WMSLayer(WMSSource wmsMapSource, string name, IPolygon maxExtent)
            : base(wmsMapSource, name,maxExtent)
        {
            init(wmsMapSource);
        }

        public WMSLayer(WMSSource wmsMapSource, string name)
            : base(wmsMapSource, name)
        {
            init(wmsMapSource);
        }

        void init(WMSSource wmsMapSource)
        {
            _wmsMapSource = wmsMapSource;
            _wmsRequest = new WMSRequest(wmsMapSource);
            _renderer = new RasterRenderer(this);
            _renderedImage = new List<IList<FrameworkElement>>();
        }

        public override void Draw()
        {
            SetCopyrightVisibility(IsDrawable ? Visibility.Visible : Visibility.Collapsed);

            if (! CheckMaxExtent() || ! CheckScale() || ! IsVisible)
            {
                Logger.Debug("WMSLayer.Draw(): Drawing was skipped.");
                return;
            }
            IEnvelope requestExtentOnMap = GetRequestExtent();
            IEnvelope requestExtent = GetProjectedExtent(requestExtentOnMap);

            Uri imageUri = _wmsRequest.GetUri(requestExtent, (int)RequestImageWidth(), (int)RequestImageHeight());

            IPolygon imgBoundary = _geoUtil.EnvelopeToPolygon(requestExtent);
            imgBoundary.SRID = _wmsMapSource.Srid;
            IList<FrameworkElement> images = _renderer.Draw(imageUri, imgBoundary);

            foreach( FrameworkElement img in images )
            {
                Children.Add(img);
            }

            _renderedImage.Add(images);
            RemoveOldImage();
        }

        void RemoveOldImage()
        {
            if ( _renderedImage.Count >= 3 )
            {
                var oldImages = _renderedImage[0];
                _renderedImage.RemoveAt(0);

                foreach (FrameworkElement image in oldImages)
                {
                    Children.Remove(image);
                    image.DataContext = null;
                }
            }
            
        }

        /// <summary>
        /// Check the map width is bigger than the WMS max width or not.
        /// </summary>
        /// <returns>request image width</returns>
        double RequestImageWidth()
        {
            return (MapCoord.ScreenWidth > _wmsMapSource.MaxWidth) 
                ? _wmsMapSource.MaxWidth : MapCoord.ScreenWidth;
        }

        /// <summary>
        /// Check the map height is bigger than the WMS max height or not.
        /// </summary>
        /// <returns>request image height</returns>
        double RequestImageHeight()
        {
            return (MapCoord.ScreenHeight > _wmsMapSource.MaxHeight) 
                ? _wmsMapSource.MaxHeight : MapCoord.ScreenHeight;
        }

        bool IsMaxWidth(double width)
        {
            return (width == _wmsMapSource.MaxWidth);
        }

        bool IsMaxHeight(double height)
        {
            return (height == _wmsMapSource.MaxHeight);
        }

        /// <summary>
        /// Check if a current map size is bigger than the WMS max size and return a requestable extent.
        /// </summary>
        /// <returns>request extent</returns>
        IEnvelope GetRequestExtent()
        {
            double imgWidth = RequestImageWidth();
            double imgHeight = RequestImageHeight();
            IEnvelope sourceExtent = CurrentExtent.EnvelopeInternal;

            if (!IsMaxWidth(imgWidth) && !IsMaxHeight(imgHeight))
            {
                return sourceExtent;
            }

            double minX = sourceExtent.MinX;
            double minY = sourceExtent.MinY;
            double maxX = sourceExtent.MaxX;
            double maxY = sourceExtent.MaxY;
            
            if (IsMaxWidth(imgWidth))
            {
                double centerX = (sourceExtent.MaxX + sourceExtent.MinX) / 2;
                double imgRatio = imgWidth / MapCoord.ScreenWidth;
                minX = centerX - sourceExtent.Width * imgRatio / 2;
                maxX = centerX + sourceExtent.Width * imgRatio / 2;
            }

            if (IsMaxHeight(imgHeight))
            {
                double centerY = (sourceExtent.MaxY + sourceExtent.MinY) / 2;
                double imgRatio = imgHeight / MapCoord.ScreenHeight;
                minY = centerY - sourceExtent.Height * imgRatio / 2;
                maxY = centerY + sourceExtent.Height * imgRatio / 2;
            }
            return new Envelope(minX, maxX, minY, maxY);

        }

        /*
        public double GetImageOffsetX()
        {
            if (_wmsMapSource.Width > _wmsMapSource.MaxWidth)
            {
                return (_wmsMapSource.Width - _wmsMapSource.MaxWidth) / 2;
            }
            else
            {
                return 0;
            }
        }

        public double GetImageOffsetY()
        {
            if (_wmsMapSource.Height > _wmsMapSource.MaxHeight)
            {
                return (_wmsMapSource.Height - _wmsMapSource.MaxHeight) / 2;
            }
            else
            {
                return 0;
            }
        }
         */


        public override XElement ToXml()
        {
            return ToXml("WMSLayer");
        }

        public override XElement ToXml(string elementName)
        {
            return base.ToXml(elementName);
        }
    }
}
